package com.hfy.music.ws;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.hfy.feign.IUserClient;
import com.hfy.model.message.*;
import com.hfy.model.po.user.UserCommentPo;
import com.hfy.model.po.together.TogetherInvitePo;
import com.hfy.music.service.ListenMusicTogetherService;
import constants.WebsocketConstants;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static constants.RedisConstants.*;

/**
 * @ author 韩
 * time: 2024/10/7 1:29
 */

@ServerEndpoint("/ws/together/{account}/{roomId}")
@Slf4j
@Component
public class WsListenMusicTogether {


    //声明session对象，通过该对象可以发送消息给指定的用户
    private Session session;
    // 存储session信息
    private static final Map<String, Session> onlineUsers = new ConcurrentHashMap<>();


    private static ApplicationContext applicationContext;

    // 不使用静态类来存储聊天室里的用户，用redis实现
    public static void setApplicationContext(ApplicationContext applicationContext) {
        WsListenMusicTogether.applicationContext = applicationContext;
    }

    private static StringRedisTemplate redisTemplate;
    private static ListenMusicTogetherService listenMusicTogetherService;
    private static IUserClient userClient;


    // 标识我是不是主人
    private Boolean isOwner;

    private String account;
    private String roomId;

    @OnOpen
    @SneakyThrows
    public void onOpen(Session session, @PathParam("account") String account, @PathParam("roomId") String roomId, EndpointConfig config)  {
        // 配置依赖
        redisTemplate = applicationContext.getBean(StringRedisTemplate.class);
        listenMusicTogetherService = applicationContext.getBean(ListenMusicTogetherService.class);
        userClient = applicationContext.getBean(IUserClient.class);
        // 获取携带的account和roomId
        this.account = account;
        this.roomId = roomId;
        this.session = session;


        // 增加一个用户在线人数并且标识出来这个是谁
        redisTemplate.opsForValue().increment(TOGETHER_COUNT_USER_KEY);
        // 把这个在线用户添加到redis里
        if (roomId == null) {
            log.error("房间号不能为空");
            throw new RuntimeException("房间号不能为空");
        }
        // 给用户在redis里设置对应的房间号，这里设置默认值0
        redisTemplate.opsForValue().setIfAbsent(TOGETHER_USER_KEY + account, "0");


        // 把成员号添加到redis里
        // 判断是不是主人，由于主人已经添加了，如果主人添加了就添加客人

        String ownerAccount = (String) redisTemplate.opsForHash().get(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_OWNER_KEY);

        // 验证身份
        if (ownerAccount != null && ownerAccount.equals(account)) {
            isOwner = true;
        } else {
            // 证明是客人
            redisTemplate.opsForHash().putIfAbsent(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_VISITOR_KEY, account);
            isOwner = false;
        }

        // 发送开启报文，
        StartRoomMessage message = new StartRoomMessage();
        message.setType(WebsocketConstants.OPERATION_START_ROOM);
//        message.setRoomId(roomId);

        UserCommentPo userInfo = userClient.getInfoByAccount(Long.valueOf(account));
        StartRoomOperationData startRoomOperationData = new StartRoomOperationData(account, userInfo.getAvatar(), userInfo.getUsername(), true);
        message.setData(startRoomOperationData);
        String jsonMessage = JSON.toJSONString(message);
        // 添加账号和对应session到map里
        onlineUsers.put(account, this.session);

        // 另一个用户接收报文
        try {
            if (isOwner) {
                // 这个另一个人的信息在前端要存储到全局变量中，不然自己的页面一刷新就没了对方的信息，只有对方再刷新他才会回来，可以写一个上线提醒，退出网页就会离线
                // 但不会退出房间

                String visitorAccount = (String) redisTemplate.opsForHash().get(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_VISITOR_KEY);
                Session visitorSession = null;
                if (visitorAccount != null) visitorSession = onlineUsers.get(visitorAccount);
                if (visitorSession != null) visitorSession.getBasicRemote().sendText(jsonMessage);
            } else {
                Session ownerSession = onlineUsers.get(ownerAccount);
                if (ownerSession != null) ownerSession.getBasicRemote().sendText(jsonMessage);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        log.info("OK");
    }


    private String getOwnerAccount() {
        return (String) redisTemplate.opsForHash().get(TOGETHER_ROOM_KEY + this.roomId, TOGETHER_HASH_OWNER_KEY);
    }

    @OnMessage
    public void onMessage(String message, @PathParam("account") String account, @PathParam("roomId") String roomId) {

        // 0. 直接向房间号发送指令，然后对房间内所有人都执行相同操作
        // 获取房间内的所有用户
        SimpleTogetherMessage commonMessage = JSON.parseObject(message, SimpleTogetherMessage.class);

        // 对报文进行解析
        Integer type = commonMessage.getType();
        if (type.equals(WebsocketConstants.OPERATION_CHANGE_MUSIC)) {
            ChangMusicMessage operationMessage = JSON.parseObject(message, ChangMusicMessage.class);

            ChangMusicOperationData data = operationMessage.getData();

            // 是切换音乐操作，进行音乐同步，redis进行存储，可以改用redis监听降低耦合，这里直接同步写了 todo
            if (data.getMusicIndex() != null) redisTemplate.opsForHash().put(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_CURRENT_MUSIC_INDEX_KEY, data.getMusicIndex());
            else redisTemplate.opsForHash().put(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_CURRENT_MUSIC_INDEX_KEY, 0);
            if (data.getMusicSheet() != null) redisTemplate.opsForHash().put(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_CURRENT_MUSIC_SHEET_KEY, data.getMusicSheet());
            else redisTemplate.opsForHash().put(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_CURRENT_MUSIC_SHEET_KEY, "");

        } else if (type.equals(WebsocketConstants.OPERATION_PLAY_MUSIC)){
            // 播放音乐报文，不需要有data
            redisTemplate.opsForHash().put(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_CURRENT_PLAY_STATUS_KEY, "true");
        } else if (type.equals(WebsocketConstants.OPERATION_PAUSE_MUSIC)){
            // 暂停音乐报文，不需要有data
            redisTemplate.opsForHash().put(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_CURRENT_PLAY_STATUS_KEY, "false");
        }  else if (type.equals(WebsocketConstants.OPERATION_POSITION_MUSIC)){
            // 调整音乐进度报文，需要有data
            String data = commonMessage.getData().getContent();
            redisTemplate.opsForHash().put(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_LAST_POSITION_KEY, data);
        }

        // todo ... 供以后的功能扩充

        else if (type.equals(WebsocketConstants.OPERATION_LOGOUT_TOGETHER)) {
            // 删除痕迹，直接删除房间
            redisTemplate.opsForHash().delete(TOGETHER_ROOM_KEY + roomId);
            // 这里关闭房间
            LambdaUpdateWrapper<TogetherInvitePo> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(TogetherInvitePo::getStatus, 3).eq(TogetherInvitePo::getRoomId, TOGETHER_ROOM_KEY + roomId);
            listenMusicTogetherService.update(updateWrapper);

        }


        // 对房间内所有用户的客户端发出报文，报文不变
        // 获取对应Session
        Session session = getSuitableSession();
        try {
            if (session != null) session.getBasicRemote().sendText(message);

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    private Session getSuitableSession() {
        if (isOwner) {
            String visitorAccount = (String) redisTemplate.opsForHash().get(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_VISITOR_KEY);
            if (visitorAccount == null) return null;
            return onlineUsers.get(visitorAccount);
        } else {
            String ownerAccount = (String) redisTemplate.opsForHash().get(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_OWNER_KEY);
            if (ownerAccount == null) return null;

            return onlineUsers.get(ownerAccount);
        }
    }

    @OnClose
    public void onClose() {
//        redisTemplate.opsForValue().decrement(TOGETHER_COUNT_USER_KEY);
        onlineUsers.remove(account);

//        if (isOwner) {
//            // 如果是主人就删除主人的redis存储
//            redisTemplate.opsForHash().delete(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_OWNER_KEY);
//        } else {
//            // 如果是客人就删除客人的redis存储
//            redisTemplate.opsForHash().delete(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_VISITOR_KEY);
//        }

    }

    @OnError
    public void onError(Session session, Throwable error) {

        log.error(error.toString());

        error.printStackTrace();
    }

}
